namespace SubhadraSolutions.Sharp.Logging.Adapters.Log4Net
{
    using log4net;
    using log4net.Appender;
    using log4net.Core;
    using log4net.Repository.Hierarchy;
    using SubhadraSolutions.Sharp.Logging.Contracts;
    using SubhadraSolutions.Sharp.Utils;
    using System;
    using ILogger = SubhadraSolutions.Sharp.Logging.Contracts.ILogger;

    public sealed class LogAsLoggerAdapter : AbstractInitializableAndDisposable, ILogger
    {
        #region Fields

        private ILog _adaptee;

        #endregion Fields

        #region Constructors

        public LogAsLoggerAdapter(ILog adaptee)
        {
            _adaptee = adaptee;
        }

        public LogAsLoggerAdapter()
        {
        }

        #endregion Constructors

        #region Properties

        public string Name
        {
            get { return Log4NetLoggerName; }
            set { Log4NetLoggerName = value; }
        }

        public string Log4NetLoggerName
        {
            get;
            set;
        }

        #endregion Properties

        #region Methods

        protected override void initializeProtected()
        {
            if (_adaptee == null)
            {
                _adaptee = LogManager.GetLogger(Log4NetLoggerName);
            }
        }

        public void Log(LogStoreItem item)
        {
            if (item != null)
            {
                Log(item.Severity, item.Categories, item);
            }
        }

        public void Log(LogItemSeverity severity, LogItemCategories categories, object logEntry)
        {
            switch (severity)
            {
                case LogItemSeverity.DEBUG:
                    _adaptee.Debug(logEntry);
                    break;

                case LogItemSeverity.INFO:
                    _adaptee.Info(logEntry);
                    break;

                case LogItemSeverity.WARN:
                    _adaptee.Warn(logEntry);
                    break;

                case LogItemSeverity.ERROR:
                    _adaptee.Error(logEntry);
                    break;

                case LogItemSeverity.FATAL:
                    _adaptee.Fatal(logEntry);
                    break;
            }
        }

        public void Log(LogItemSeverity severity, LogItemCategories categories, string message, Exception exception)
        {
            switch (severity)
            {
                case LogItemSeverity.DEBUG:
                    _adaptee.Debug(message, exception);
                    break;

                case LogItemSeverity.INFO:
                    _adaptee.Info(message, exception);
                    break;

                case LogItemSeverity.WARN:
                    _adaptee.Warn(message, exception);
                    break;

                case LogItemSeverity.ERROR:
                    _adaptee.Error(message, exception);
                    break;

                case LogItemSeverity.FATAL:
                    _adaptee.Fatal(message, exception);
                    break;
            }
        }

        public void Log(ILogItem logEntry)
        {
            if (logEntry != null)
            {
                Log(logEntry.Severity, logEntry.Categories, logEntry);
            }
        }

        protected override void Dispose(bool disposing)
        {
            flushBuffers(_adaptee);
        }

        private static void flushBuffers(ILoggerWrapper log)
        {
            var logger = log.Logger as Logger;
            if (logger != null)
            {
                foreach (IAppender appender in logger.Appenders)
                {
                    var buffered = appender as BufferingAppenderSkeleton;
                    if (buffered != null)
                    {
                        buffered.Flush();
                    }
                }
            }
        }

        #endregion Methods
    }
}